home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / Utilities.java < prev    next >
Text File  |  1998-06-30  |  15KB  |  455 lines

  1. /*
  2.  * @(#)Utilities.java    1.17 98/04/09
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.text;
  21.  
  22. import java.awt.Rectangle;
  23. import java.awt.Graphics;
  24. import java.awt.FontMetrics;
  25. import java.text.*;
  26.  
  27. /**
  28.  * A collection of methods to deal with various text
  29.  * related activities.
  30.  * 
  31.  * @author  Timothy Prinzing
  32.  * @version 1.17 04/09/98
  33.  */
  34. public class Utilities {
  35.  
  36.     /**
  37.      * Draws the given text, expanding any tabs that are contained
  38.      * using the given tab expansion technique.  This particular
  39.      * implementation renders in a 1.1 style coordinate system
  40.      * where ints are used and 72dpi is assumed.
  41.      * 
  42.      * @param s  the source of the text
  43.      * @param x  the X origin >= 0
  44.      * @param y  the Y origin >= 0
  45.      * @param g  the graphics context
  46.      * @param e  how to expand the tabs.  If this value is null, 
  47.      *   tabs will be expanded as a space character.
  48.      * @param startOffset starting offset of the text in the document >= 0
  49.      * @returns  the X location at the end of the rendered text
  50.      */
  51.     public static final int drawTabbedText(Segment s, int x, int y, Graphics g, 
  52.                        TabExpander e, int startOffset) {
  53.     FontMetrics metrics = g.getFontMetrics();
  54.     int nextX = x;
  55.     char[] txt = s.array;
  56.     int flushLen = 0;
  57.     int flushIndex = s.offset;
  58.     int n = s.offset + s.count;
  59.     for (int i = s.offset; i < n; i++) {
  60.         if (txt[i] == '\t') {
  61.         if (flushLen > 0) {
  62.             g.drawChars(txt, flushIndex, flushLen, x, y);
  63.             flushLen = 0;
  64.         }
  65.         flushIndex = i + 1;
  66.         if (e != null) {
  67.             nextX = (int) e.nextTabStop((float) nextX, startOffset + i - s.offset);
  68.         } else {
  69.             nextX += metrics.charWidth(' ');
  70.         }
  71.         x = nextX;
  72.         } else if ((txt[i] == '\n') || (txt[i] == '\r')) {
  73.         if (flushLen > 0) {
  74.             g.drawChars(txt, flushIndex, flushLen, x, y);
  75.             flushLen = 0;
  76.         }
  77.         flushIndex = i + 1;
  78.         x = nextX;
  79.         } else {
  80.         flushLen += 1;
  81.         nextX += metrics.charWidth(txt[i]);
  82.         }
  83.     } 
  84.     if (flushLen > 0) {
  85.         g.drawChars(txt, flushIndex, flushLen, x, y);
  86.     }
  87.     return nextX;
  88.     }
  89.  
  90.     /**
  91.      * Determines the width of the given segment of text taking tabs 
  92.      * into consideration.  This is implemented in a 1.1 style coordinate 
  93.      * system where ints are used and 72dpi is assumed.
  94.      *
  95.      * @param s  the source of the text
  96.      * @param metrics the font metrics to use for the calculation
  97.      * @param x  the X origin >= 0
  98.      * @param e  how to expand the tabs.  If this value is null, 
  99.      *   tabs will be expanded as a space character.
  100.      * @param startOffset starting offset of the text in the document >= 0
  101.      * @returns  the width of the text
  102.      */
  103.     public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, int x, 
  104.                            TabExpander e, int startOffset) {
  105.     int nextX = x;
  106.     char[] txt = s.array;
  107.     int n = s.offset + s.count;
  108.     for (int i = s.offset; i < n; i++) {
  109.         if (txt[i] == '\t') {
  110.         if (e != null) {
  111.             nextX = (int) e.nextTabStop((float) nextX,
  112.                         startOffset + i - s.offset);
  113.         } else {
  114.             nextX += metrics.charWidth(' ');
  115.         }
  116.         } else {
  117.         nextX += metrics.charWidth(txt[i]);
  118.         }
  119.     }
  120.     return nextX - x;
  121.     }
  122.  
  123.     /**
  124.      * Determines the relative offset into the given text that
  125.      * best represents the given span in the view coordinate
  126.      * system.  This is implemented in a 1.1 style coordinate 
  127.      * system where ints are used and 72dpi is assumed.
  128.      *
  129.      * @param s  the source of the text
  130.      * @param metrics the font metrics to use for the calculation
  131.      * @param x0 the starting view location representing the start
  132.      *   of the given text >= 0.
  133.      * @param x  the target view location to translate to an
  134.      *   offset into the text >= 0.
  135.      * @param e  how to expand the tabs.  If this value is null, 
  136.      *   tabs will be expanded as a space character.
  137.      * @param startOffset starting offset of the text in the document >= 0
  138.      * @returns  the offset into the text >= 0
  139.      */
  140.     public static final int getTabbedTextOffset(Segment s, FontMetrics metrics, 
  141.                          int x0, int x, TabExpander e,
  142.                          int startOffset) {
  143.  
  144.     int currX = x0;
  145.     int nextX = currX;
  146.     char[] txt = s.array;
  147.     int n = s.offset + s.count;
  148.     for (int i = s.offset; i < n; i++) {
  149.         if (txt[i] == '\t') {
  150.         if (e != null) {
  151.             nextX = (int) e.nextTabStop((float) nextX,
  152.                         startOffset + i - s.offset);
  153.         } else {
  154.             nextX += metrics.charWidth(' ');
  155.         }
  156.         } else {
  157.         nextX += metrics.charWidth(txt[i]);
  158.         }
  159.         if ((x >= currX) && (x < nextX)) {
  160.         // found the hit position... return the appropriate side
  161.         if ((x - currX) < (nextX - x)) {
  162.             return i - s.offset;
  163.         } else {
  164.             return i + 1 - s.offset;
  165.         }
  166.         }
  167.         currX = nextX;
  168.     }
  169.  
  170.     // didn't find, return end offset
  171.     return s.count;
  172.     }
  173.  
  174.     /**
  175.      * Determine where to break the given text to fit
  176.      * within the the given span.  This trys to find a
  177.      * whitespace boundry.
  178.      * @param s  the source of the text
  179.      * @param metrics the font metrics to use for the calculation
  180.      * @param x0 the starting view location representing the start
  181.      *   of the given text.
  182.      * @param x  the target view location to translate to an
  183.      *   offset into the text.
  184.      * @param e  how to expand the tabs.  If this value is null, 
  185.      *   tabs will be expanded as a space character.
  186.      * @param startOffset starting offset in the document of the text
  187.      * @returns  the offset into the given text.
  188.      */
  189.     public static final int getBreakLocation(Segment s, FontMetrics metrics,
  190.                          int x0, int x, TabExpander e,
  191.                          int startOffset) {
  192.  
  193.     int index = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset);
  194.     for (int i = s.offset + Math.min(index, s.count - 1); 
  195.          i >= s.offset; i--) {
  196.         
  197.         char ch = s.array[i];
  198.         if (Character.isWhitespace(ch)) {
  199.         // found whitespace, break here
  200.         index = i - s.offset + 1;
  201.         break;
  202.         }
  203.     }
  204.     return index;
  205.     }
  206.  
  207.     /**
  208.      * Determines the starting row model position of the row that contains
  209.      * the specified model position.  Assumes the row(s) are currently
  210.      * displayed in a view.
  211.      *
  212.      * @param c the editor
  213.      * @param offs the offset in the document >= 0
  214.      * @return the position >= 0
  215.      * @exception BadLocationException if the offset is out of range
  216.      */
  217.     public static final int getRowStart(JTextComponent c, int offs) throws BadLocationException {
  218.     Rectangle r = c.modelToView(offs);
  219.     int lastOffs = offs;
  220.     int y = r.y;
  221.     while ((r != null) && (y == r.y)) {
  222.         offs = lastOffs;
  223.         lastOffs -= 1;
  224.         r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
  225.     }
  226.     return offs;
  227.     }
  228.  
  229.     /**
  230.      * Determines the ending row model position of the row that contains
  231.      * the specified model position.  Assumes the row(s) are currently
  232.      * displayed in a view.
  233.      *
  234.      * @param c the editor
  235.      * @param offs the offset in the document >= 0
  236.      * @return the position >= 0
  237.      * @exception BadLocationException if the offset is out of range
  238.      */
  239.     public static final int getRowEnd(JTextComponent c, int offs) throws BadLocationException {
  240.     Rectangle r = c.modelToView(offs);
  241.     int n = c.getDocument().getLength();
  242.     int lastOffs = offs;
  243.     int y = r.y;
  244.     while ((r != null) && (y == r.y)) {
  245.         offs = lastOffs;
  246.         lastOffs += 1;
  247.         r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
  248.     }
  249.     return offs;
  250.     }
  251.  
  252.     /**
  253.      * Determines the position in the model that is closest to the given 
  254.      * view location in the row above.
  255.      *
  256.      * @param c the editor
  257.      * @param offs the offset in the document >= 0
  258.      * @param x the X coordinate >= 0
  259.      * @return the model position >= 0
  260.      * @exception BadLocationException if the offset is out of range
  261.      */
  262.     public static final int getPositionAbove(JTextComponent c, int offs, int x) throws BadLocationException {
  263.     int lastOffs = getRowStart(c, offs) - 1;
  264.     int bestSpan = Short.MAX_VALUE;
  265.     int y = 0;
  266.     Rectangle r = null;
  267.     if (lastOffs >= 0) {
  268.         r = c.modelToView(lastOffs);
  269.         y = r.y;
  270.     }
  271.     while ((r != null) && (y == r.y)) {
  272.         int span = Math.abs(r.x - x);
  273.         if (span < bestSpan) {
  274.         offs = lastOffs;
  275.         bestSpan = span;
  276.         }
  277.         lastOffs -= 1;
  278.         r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
  279.     }
  280.     return offs;
  281.     }
  282.  
  283.     /**
  284.      * Determines the position in the model that is closest to the given 
  285.      * view location in the row below.
  286.      *
  287.      * @param c the editor
  288.      * @param offs the offset in the document >= 0
  289.      * @param x the X coordinate >= 0
  290.      * @return the model position >= 0
  291.      * @exception BadLocationException if the offset is out of range
  292.      */
  293.     public static final int getPositionBelow(JTextComponent c, int offs, int x) throws BadLocationException {
  294.     int lastOffs = getRowEnd(c, offs) + 1;
  295.     int bestSpan = Short.MAX_VALUE;
  296.     int n = c.getDocument().getLength();
  297.     int y = 0;
  298.     Rectangle r = null;
  299.     if (lastOffs <= n) {
  300.         r = c.modelToView(lastOffs);
  301.         y = r.y;
  302.     }
  303.     while ((r != null) && (y == r.y)) {
  304.         int span = Math.abs(x - r.x);
  305.         if (span < bestSpan) {
  306.         offs = lastOffs;
  307.         bestSpan = span;
  308.         }
  309.         lastOffs += 1;
  310.         r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
  311.     }
  312.     return offs;
  313.     }
  314.  
  315.     /**
  316.      * Determines the start of a word for the given model location.
  317.      * Uses BreakIterator.getWordInstance() to actually get the words.
  318.      * 
  319.      * @param c the editor
  320.      * @param offs the offset in the document >= 0
  321.      * @returns the location in the model of the word start >= 0.
  322.      * @exception BadLocationException if the offset is out of range
  323.      */
  324.     public static final int getWordStart(JTextComponent c, int offs) throws BadLocationException {
  325.     Document doc = c.getDocument();
  326.     Element line = getParagraphElement(c, offs);
  327.     int lineStart = line.getStartOffset();
  328.     int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
  329.     
  330.     String s = doc.getText(lineStart, lineEnd - lineStart);
  331.     if(s != null && s.length() > 0) {
  332.         BreakIterator words = BreakIterator.getWordInstance();
  333.         words.setText(s);
  334.         int wordPosition = offs - lineStart;
  335.         if(wordPosition >= words.last()) {
  336.         wordPosition = words.last() - 1;
  337.         } 
  338.         words.following(wordPosition);
  339.         offs = lineStart + words.previous();
  340.     }
  341.     return offs;
  342.     }
  343.  
  344.     /**
  345.      * Determines the end of a word for the given location.
  346.      * Uses BreakIterator.getWordInstance() to actually get the words.
  347.      * 
  348.      * @param c the editor
  349.      * @param offs the offset in the document >= 0
  350.      * @returns the location in the model of the word end >= 0.
  351.      * @exception BadLocationException if the offset is out of range
  352.      */
  353.     public static final int getWordEnd(JTextComponent c, int offs) throws BadLocationException {
  354.     Document doc = c.getDocument();
  355.     Element line = getParagraphElement(c, offs);
  356.     int lineStart = line.getStartOffset();
  357.     int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
  358.     
  359.     String s = doc.getText(lineStart, lineEnd - lineStart);
  360.     if(s != null && s.length() > 0) {
  361.         BreakIterator words = BreakIterator.getWordInstance();
  362.         words.setText(s);
  363.         int wordPosition = offs - lineStart;
  364.         if(wordPosition >= words.last()) {
  365.         wordPosition = words.last() - 1;
  366.         } 
  367.         offs = lineStart + words.following(wordPosition);
  368.     }
  369.     return offs;
  370.     }
  371.  
  372.     /**
  373.      * Determines the start of the next word for the given location.
  374.      * Uses BreakIterator.getWordInstance() to actually get the words.
  375.      * 
  376.      * @param c the editor
  377.      * @param offs the offset in the document >= 0
  378.      * @returns the location in the model of the word start >= 0.
  379.      * @exception BadLocationException if the offset is out of range
  380.      */
  381.     public static final int getNextWord(JTextComponent c, int offs) throws BadLocationException {
  382.     Document doc = c.getDocument();
  383.     Element line = getParagraphElement(c, offs);
  384.     int lineStart = line.getStartOffset();
  385.     int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
  386.     String s = doc.getText(lineStart, lineEnd - lineStart);
  387.     if(s != null && s.length() > 0) {
  388.         BreakIterator words = BreakIterator.getWordInstance();
  389.         words.setText(s);
  390.         int wordPosition = offs - lineStart;
  391.         if(wordPosition >= words.last()) {
  392.         wordPosition = words.last() - 1;
  393.         } 
  394.         words.following(wordPosition);
  395.         offs = lineStart + words.next();
  396.     }
  397.     return offs;
  398.     }
  399.  
  400.     /**
  401.      * Determine the start of the next word for the given location.
  402.      * Uses BreakIterator.getWordInstance() to actually get the words.
  403.      * 
  404.      * @param c the editor
  405.      * @param offs the offset in the document >= 0
  406.      * @returns the location in the model of the word start >= 0.
  407.      * @exception BadLocationException if the offset is out of range
  408.      */
  409.     public static final int getPreviousWord(JTextComponent c, int offs) throws BadLocationException {
  410.     Document doc = c.getDocument();
  411.     Element line = getParagraphElement(c, offs);
  412.     int lineStart = line.getStartOffset();
  413.     int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
  414.     String s = doc.getText(lineStart, lineEnd - lineStart);
  415.     if(s != null && s.length() > 0) {
  416.         BreakIterator words = BreakIterator.getWordInstance();
  417.         words.setText(s);
  418.         int wordPosition = offs - lineStart;
  419.         if(wordPosition >= words.last()) {
  420.         wordPosition = words.last() - 1;
  421.         } 
  422.         words.following(wordPosition);
  423.         if (offs == (lineStart + words.previous())) {
  424.         words.previous();
  425.         int o = words.previous();
  426.         if (o == BreakIterator.DONE) {
  427.             offs = lineStart;
  428.         } else {
  429.             offs = lineStart + o;
  430.         }
  431.         }
  432.     }
  433.     return offs;
  434.     }
  435.  
  436.     /**
  437.      * Determines the element to use for a paragraph/line.
  438.      *
  439.      * @param c the editor
  440.      * @param offs the starting offset in the document >= 0
  441.      * @return the element
  442.      */
  443.     public static final Element getParagraphElement(JTextComponent c, int offs) {
  444.     Document doc = c.getDocument();
  445.     if (doc instanceof StyledDocument) {
  446.         return ((StyledDocument)doc).getParagraphElement(offs);
  447.     }
  448.     Element map = doc.getDefaultRootElement();
  449.     int index = map.getElementIndex(offs);
  450.     Element paragraph = map.getElement(index);
  451.     return paragraph;
  452.     }
  453.  
  454. }
  455.